#include "image_data.h"
#include "assert.h"
using namespace std;

void image_data::extract_patch(int x, int y, int rf_size, double *p) const
{
	assert(x >=0 && x+rf_size-1 < get_height());
	assert(y >=0 && y+rf_size-1 < get_width());
	for (int c = 0; c < get_channels(); ++c)
		for (int i = 0; i < rf_size; ++i)
			for (int j = 0; j < rf_size; ++j) {
				double val = (double)a[c][x+i][y+j];
				p[c*rf_size*rf_size + i*rf_size + j] = val;
			}
}

void image_data::init_a()
{
	a = new int**[channels];
	for (int c = 0; c < channels; ++c) {
		a[c] = new int*[height];
		for (int i = 0; i < height; ++i) {
			a[c][i] = new int[width];
			for (int j = 0; j < width; ++j)
				a[c][i][j] = 0;
		}
	}
}

image_data::image_data(int channels, int height, int width)
{
	this->channels = channels;
	this->height = height;
	this->width = width;
	init_a();
}

image_data::image_data(const image_data &img)
{
	channels = img.get_channels();
	height = img.get_height();
	width = img.get_width();
	label = img.get_label();
	init_a();
	for (int c = 0; c < channels; ++c)
		for (int i = 0; i < height; ++i)
			for (int j = 0; j < width; ++j)
				set_pixel(c, i, j, img.get_pixel(c, i, j));
}

image_data::~image_data()
{
	for (int c = 0; c < channels; ++c) {
		for (int i = 0; i < height; ++i)
			delete a[c][i];
		delete a[c];
	}
	delete a;
}

void image_data::set_label(int label)
{
	this->label = label;
}


void image_data::set_pixel(int channel, int pos_x, int pos_y, int pixel)
{
	assert(channel >= 0 && channel < channels);
	assert(pos_x >= 0 && pos_x < height);
	assert(pos_y >= 0 && pos_y < width);
	assert(pixel >= 0 && pixel < 256);
	a[channel][pos_x][pos_y] = pixel;
}

int image_data::get_pixel(int channel, int pos_x, int pos_y) const
{
	assert(channel >= 0 && channel < channels);
	assert(pos_x >= 0 && pos_x < height);
	assert(pos_y >= 0 && pos_y < width);
	return a[channel][pos_x][pos_y];
}

int image_data::get_label() const
{
	return label;
}

int image_data::get_channels() const
{
	return channels;
}

int image_data::get_height() const
{
	return height;
}

int image_data::get_width() const
{
	return width;
}

int image_data::get_size() const
{
	return channels * height * width;
}

void image_data::operator =(const image_data &tmp)
{
	assert(false);
}

int image_data_set::load_bin(const char *fname)
{
	FILE *f = fopen(fname, "rb");
	if (f == NULL) {
		printf("image_data_set::load_bin: cannot open file %s!\n", fname);
		return 0;
	}
	int channels, height, width;
	if (fread(&channels, 4, 1, f) == 0) {
		printf("image_data_set::load_bin: error while reading <channels>!\n");
		return 0;
	}
	int n = 0;
	while (1) {
		int label;
		if (fread(&label, 4, 1, f) == 0)
			break;
//printf("read label=%d\n", label);
		if (fread(&height, 4, 1, f) == 0) {
			printf("image_data_set::load_bin: error while reading <height>!\n");
			return 0;
		}
		if (fread(&width, 4, 1, f) == 0) {
			printf("image_data_set::load_bin: error while reading <width>!\n");
			return 0;
		}
		image_data cur(channels, height, width);
		cur.set_label(label);
		unsigned char *buf = new unsigned char[height * width * channels];
		if (fread(buf, height * width * channels, 1, f) == 0) {
			printf("image_data_set::load_bin: error while reading <data>!\n");
			return 0;
		}
		//printf("load_image_data: label=%d\n", img[n].label);
		for (int c = 0; c < channels; ++c)
			for (int i = 0; i < height; ++i)
				for (int j = 0; j < width; ++j) {
					int k = c * height * width + i * width + j;
					cur.set_pixel(c, i, j, (int)buf[k]);
				}
		delete buf;
//printf("set label=%d\n", cur.get_label());
		img.push_back(cur);
		++n;
		if (n % 200 == 0)
			printf(" - loaded %d bin images.\n", n);
	}
	fclose(f);
	return n;
}

int image_data_set::load_t200(const char *fname)
{
	FILE *f = fopen(fname, "r");
	if (f == NULL) {
		printf("image_data_set::load_t200: cannot open file %s!\n", fname);
		return 0;
	}
	int n = 0;
	while (1) {
		image_data cur(3, 200, 200);
		int label;
		if (fscanf(f, "%d", &label) < 1)
			break;
		cur.set_label(label);
		for (int c = 0; c < 3; ++c)
			for (int i = 0; i < 200; ++i)
				for (int j = 0; j < 200; ++j) {
					int x;
					if (fscanf(f, "%d", &x) < 1) {
						printf("image_data_set::load_t200: error while reading <data>!\n");
						return 0;
					}
					cur.set_pixel(c, i, j, x);
				}
		img.push_back(cur);
		++n;
	}
	fclose(f);
	return n;
}

int image_data_set::load_txt(const char *fname)
{
	FILE *f = fopen(fname, "r");
	if (f == NULL) {
		printf("image_data_set::load_t200: cannot open file %s!\n", fname);
		return 0;
	}
	int channels, height, width;
	if (fscanf(f, "%d%d%d", &channels, &height, &width) < 3)
		return 0;
	int n = 0;
	while (1) {
		int label;
		if (fscanf(f, "%d", &label) < 1)
			break;
		image_data cur(channels, height, width);
		cur.set_label(label);
		for (int c = 0; c < channels; ++c)
			for (int i = 0; i < height; ++i)
				for (int j = 0; j < width; ++j) {
					int x;
					if (fscanf(f, "%d", &x) < 1) {
						printf("image_data_set::load_txt: error while reading <data>!\n");
						return 0;
					}
					cur.set_pixel(c, i, j, x);
				}
		img.push_back(cur);
		++n;
	}
	fclose(f);
	return n;
}

int image_data_set::load_bmp(const char *path, int max_id)
{
	int cnt = 0;
	for (int id = 0; id <= max_id; ++id) {
		char filename[999];
		sprintf(filename, "%s\\%d.bmp", path, id);
		FILE *f = fopen(filename, "rb");
		if (f == NULL) {
			//printf("no %s!\n", filename);
			continue;
		}
		printf("found bitmap %s...\n", filename);

		unsigned char info[54];
		fread(info, sizeof(unsigned char), 54, f);
		int width = *(int*)&info[18];
		int height = *(int*)&info[22];

		int size = 3 * width * height;
		unsigned char* data = new unsigned char[size];
		fread(data, sizeof(unsigned char), size, f);
		fclose(f);

		image_data cur(3, height, width);
		for (int c = 0; c < 3; ++c)
			for (int i = 0; i < height; ++i)
				for (int j = 0; j < width; ++j)
					cur.set_pixel(c, i, j, (int)data[(j*width + i)*3 + c]);
		img.push_back(cur);
	}
    return cnt;
}

int image_data_set::load_bmp_list(const char *list_file)
{
	vector<string> files;
	string path(list_file);
	while (path.size() > 0 && path[path.size()-1] != '\\' && path[path.size()-1] != '/')
		path = path.substr(0, path.size()-1);
	
	FILE *f = fopen(list_file, "r");
	char buf[999];
	if (f == NULL) {
		printf("load_bmp_list: cannot open list file!\n");
		return 0;
	}
	while (!feof(f)) {
		if (fgets(buf, 999, f) == NULL)
			break;
		int len = strlen(buf);
		while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r' || buf[len-1] == ' '))
			--len;
		buf[len] = '\0';
		if (len > 0) {
			string p = path;
			p += string(buf);
			p += ".bmp";
			files.push_back(p);
		}
	}
	fclose(f);

	int cnt = 0;
	for (int id = 0; id < (int)files.size(); ++id) {
		FILE *f = fopen(files[id].c_str(), "rb");
		if (f == NULL) {
			printf(" [no file %s...]\n", files[id].c_str());
			continue;
		}
		printf("loading file %s...\n", files[id].c_str());
		unsigned char info[54];
		fread(info, sizeof(unsigned char), 54, f);
		int width = *(int*)&info[18];
		int height = *(int*)&info[22];

		int size = 3 * width * height;
		unsigned char* data = new unsigned char[size];
		fread(data, sizeof(unsigned char), size, f);
		fclose(f);

		image_data cur(3, height, width);
		for (int c = 0; c < 3; ++c)
			for (int i = 0; i < height; ++i)
				for (int j = 0; j < width; ++j)
					cur.set_pixel(c, i, j, (int)data[(j*width + i)*3 + c]);
		img.push_back(cur);
	}
    return cnt;
}

void write_int_to_bin(int x, FILE *f)
{
	if (fwrite(&x, 4, 1, f) < 1) {
		printf("fail to write %d to file!\n", x);
		exit(1);
	}
}

void image_data_set::save_bin(const char *fname)
{
	FILE *f = fopen(fname, "wb");
	if (f == NULL) {
		printf("image_data_set::save_bin: cannot open file %s!\n", fname);
		return;
	}
	if (size() == 0) {
		fclose(f);
		return;
	}
	write_int_to_bin(img[0].get_channels(), f);
	for (int k = 0; k < img.size(); ++k) {
		write_int_to_bin(img[k].get_label(), f);
		write_int_to_bin(img[k].get_height(), f);
		write_int_to_bin(img[k].get_width(), f);
		int sz = img[k].get_size();
		unsigned char *buf = new unsigned char[sz];
		for (int c = 0; c < img[k].get_channels(); ++c)
			for (int i = 0; i < img[k].get_height(); ++i)
				for (int j = 0; j < img[k].get_width(); ++j) {
					int p = c * img[k].get_height() * img[k].get_width() + i * img[k].get_width() + j;
					buf[p] = (unsigned char) img[k].get_pixel(c, i, j);
				}
		fwrite(buf, sz, 1, f);
		delete buf;
	}
	fclose(f);
}

void image_data_set::save_t200(const char *fname)
{
	for (int k = 0; k < img.size(); ++k) {
		assert(img[k].get_channels() == 3);
		assert(img[k].get_width() == 200);
		assert(img[k].get_height() == 200);
	}
	FILE *f = fopen(fname, "w");
	if (f == NULL) {
		printf("image_data_set::save_t200: cannot open file %s!\n", fname);
		return;
	}
	for (int k = 0; k < img.size(); ++k) {
		fprintf(f, "%d", img[k].get_label());
		for (int c = 0; c < img[k].get_channels(); ++c)
			for (int i = 0; i < img[k].get_height(); ++i)
				for (int j = 0; j < img[k].get_width(); ++j)
					fprintf(f, " %d", img[k].get_pixel(c, i, j));
		fprintf(f, "\n");
	}
	fclose(f);
}

void image_data_set::save_txt(const char *fname)
{
	assert(img.size() > 0);
	for (int k = 0; k < img.size(); ++k) {
		assert(img[k].get_channels() == img[0].get_channels());
		assert(img[k].get_width() == img[0].get_width());
		assert(img[k].get_height() == img[0].get_height());
	}
	FILE *f = fopen(fname, "w");
	if (f == NULL) {
		printf("image_data_set::save_t200: cannot open file %s!\n", fname);
		return;
	}
	fprintf(f, "%d %d %d\n", img[0].get_channels(), img[0].get_height(), img[0].get_width());
	for (int k = 0; k < img.size(); ++k) {
		fprintf(f, "%d", img[k].get_label());
		for (int c = 0; c < img[k].get_channels(); ++c)
			for (int i = 0; i < img[k].get_height(); ++i)
				for (int j = 0; j < img[k].get_width(); ++j)
					fprintf(f, " %d", img[k].get_pixel(c, i, j));
		fprintf(f, "\n");
	}
	fclose(f);
}

image_data& image_data_set::operator [](int i)
{
	assert(i >= 0 && i < img.size());
	return img[i];
}

int image_data_set::size()
{
	return (int)img.size();
}